﻿using TulingMember.Core;
using Furion;
using Furion.DatabaseAccessor;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Linq;
using System.Linq.Expressions;
using Furion.FriendlyException;
using Microsoft.EntityFrameworkCore.Metadata; 
using Yitter.IdGenerator;

namespace TulingMember.EntityFramework.Core
{
    [AppDbContext("SqlServerConnectionString")]
    public class DefaultDbContext : AppDbContext<DefaultDbContext>, IMultiTenantOnTable, IModelBuilderFilter

    {
        //审计日志排除字段
        string[] AuditExclude = {
             nameof(DEntityBase.Id)
            ,nameof(DEntityBase.CreatedUserId)
            ,nameof(DEntityBase.CreatedUserName)
            ,nameof(DEntityBase.CreatedTime)
            ,nameof(DEntityBase.UpdatedUserId)
            ,nameof(DEntityBase.UpdatedUserName)
            ,nameof(DEntityBase.UpdatedTime)
        };
 
        public DefaultDbContext(DbContextOptions<DefaultDbContext> options) : base(options)
        {
             EnabledEntityStateTracked= false;
               // 忽略空值更新
            InsertOrUpdateIgnoreNullValues = true;
        }

        /// <summary>
        /// 获取租户Id
        /// </summary>
        /// <returns></returns>
        public object GetTenantId()
        {
            if (App.User == null) return null;
            return Convert.ToInt64(App.User.FindFirst(ClaimConst.TENANT_ID)?.Value);
        }

        /// <summary>
        /// 配置租户Id过滤器
        /// </summary>
        /// <param name="modelBuilder"></param>
        /// <param name="entityBuilder"></param>
        /// <param name="dbContext"></param>
        /// <param name="dbContextLocator"></param>
        public void OnCreating(ModelBuilder modelBuilder, EntityTypeBuilder entityBuilder, DbContext dbContext, Type dbContextLocator)
        {
            // 配置租户Id以及假删除过滤器
            LambdaExpression expression = TenantIdAndFakeDeleteQueryFilterExpression(entityBuilder, dbContext);
            if (expression != null)
                entityBuilder.HasQueryFilter(expression);
        }


        protected override void SavingChangesEvent(DbContextEventData eventData, InterceptionResult<int> result)
        { 
            // 获取当前事件对应上下文
            var dbContext = eventData.Context;

            // 获取所有新增和更新的实体
            var entities = dbContext.ChangeTracker.Entries().Where(u => u.Entity.GetType() != typeof(IPLog) &&( u.State == EntityState.Added || u.State == EntityState.Modified || u.State == EntityState.Deleted)).ToList();
            var userId = App.User.FindFirst(ClaimConst.CLAINM_USERID)?.Value;
            var userName = App.User.FindFirst(ClaimConst.CLAINM_NAME)?.Value;
            if (entities == null || entities.Count < 1) return; 
            foreach (var entity in entities)
            { 
               
                if (entity.Entity.GetType().IsSubclassOf(typeof(DEntityTenant)))
                {
                    var obj = entity.Entity as DEntityTenant;
                    switch (entity.State)
                    {
                        // 自动设置租户Id
                        case EntityState.Added:
                            var tenantId = entity.Property(nameof(Entity.TenantId)).CurrentValue;
                            if (tenantId == null || (long)tenantId == 0)
                                entity.Property(nameof(Entity.TenantId)).CurrentValue = GetTenantId();

                            obj.Id = obj.Id == 0 ? YitIdHelper.NextId() : obj.Id;
                            obj.CreatedTime = DateTimeOffset.Now;
                            if (!string.IsNullOrEmpty(userId))
                            {
                                obj.CreatedUserId = long.Parse(userId);
                                obj.CreatedUserName = userName;
                            }
                            
                            obj.UpdatedUserId = null;
                            obj.UpdatedTime = null;
                            obj.UpdatedUserName = null;
                            break;
                        case EntityState.Modified:
                            // 排除租户Id
                            entity.Property(nameof(DEntityTenant.TenantId)).IsModified = false;
                            // 排除创建人
                            entity.Property(nameof(DEntityTenant.CreatedUserId)).IsModified = false;
                            entity.Property(nameof(DEntityTenant.CreatedUserName)).IsModified = false;
                            // 排除创建日期
                            entity.Property(nameof(DEntityTenant.CreatedTime)).IsModified = false;

                            obj.UpdatedTime = DateTimeOffset.Now;
                            if (!string.IsNullOrEmpty(userId))
                            {
                                obj.UpdatedUserId = long.Parse(userId);
                                obj.UpdatedUserName = userName;
                            }
                            break;
                    }
                }
                else if(entity.Entity.GetType().IsSubclassOf(typeof(DEntityBase<long, MultiTenantDbContextLocator>)))
                {
                    var obj = entity.Entity as DEntityBase<long, MultiTenantDbContextLocator>;
                    if (entity.State == EntityState.Added)
                    {
                        obj.Id = obj.Id == 0 ? YitIdHelper.NextId() : obj.Id;
                        obj.CreatedTime = DateTimeOffset.Now;
                        if (!string.IsNullOrEmpty(userId))
                        {
                            obj.CreatedUserId = long.Parse(userId);
                            obj.CreatedUserName = userName;

                        }
                    }
                    else if (entity.State == EntityState.Modified)
                    {
                        // 排除创建人
                        entity.Property(nameof(DEntityTenant.CreatedUserId)).IsModified = false;
                        entity.Property(nameof(DEntityTenant.CreatedUserName)).IsModified = false;
                        // 排除创建日期
                        entity.Property(nameof(DEntityTenant.CreatedTime)).IsModified = false;
                        obj.UpdatedTime = DateTimeOffset.Now;
                        if (!string.IsNullOrEmpty(userId))
                        {
                            obj.UpdatedUserId = long.Parse(userId);
                            obj.UpdatedUserName = userName;
                        }
                    }
                }
                else if (entity.Entity.GetType().IsSubclassOf(typeof(DEntityBase)))
                {
                    var obj = entity.Entity as DEntityBase;
                    if (entity.State == EntityState.Added)
                    {
                        obj.Id = obj.Id == 0 ? YitIdHelper.NextId() : obj.Id;
                        obj.CreatedTime = DateTimeOffset.Now;
                        if (!string.IsNullOrEmpty(userId))
                        {
                            obj.CreatedUserId = long.Parse(userId);
                            obj.CreatedUserName = userName;
                        }
                    }
                    else if (entity.State == EntityState.Modified)
                    {
                        // 排除创建人
                        entity.Property(nameof(DEntityBase.CreatedUserId)).IsModified = false;
                        entity.Property(nameof(DEntityBase.CreatedUserName)).IsModified = false;
                        // 排除创建日期
                        entity.Property(nameof(DEntityBase.CreatedTime)).IsModified = false;

                        obj.UpdatedTime = DateTimeOffset.Now;
                        if (!string.IsNullOrEmpty(userId))
                        {
                            obj.UpdatedUserId = long.Parse(userId);
                            obj.UpdatedUserName = userName;
                        }
                    }
                }
                //如需审计
                if (entity.Entity.GetType().IsDefined(typeof(TableAuditAttribute), true))
                {
                    var entityType = entity.Entity.GetType();
                    // 获取所有实体有效属性，排除 [NotMapper] 属性
                    var props = entity.OriginalValues.Properties;
                    // 获取实体当前（现在）的值
                    var currentValues = entity.CurrentValues;
                    // 获取数据库中实体的值
                    var databaseValues = entity.GetDatabaseValues();
                    // 遍历所有属性
                    var batchNo = YitIdHelper.NextId();
                    foreach (var prop in props)
                    {
                        // 获取属性名
                        var propName = prop.Name;
                        if (AuditExclude.Contains(propName))
                        {
                            continue;
                        }
                        // 获取现在的实体值
                        var newValue = currentValues[propName] ?? "";

                        object oldValue = "";
                        // 如果是新增数据，则 databaseValues 为空，所以需要判断一下
                        if (databaseValues != null)
                        {
                            oldValue = databaseValues[propName] ?? "";
                        }
                        if (!oldValue.Equals(newValue))
                        {
                            LogAudit.Log(new Audit
                            {
                                Table = entityType.Name,    // 表名
                                Column = propName,  // 更新的列
                                NewValue = newValue,    // 新值
                                OldValue = oldValue,    // 旧值
                                CreatedTime = DateTime.Now, // 操作时间
                                CreatedUserId = long.Parse(userId),    // 操作人
                                CreatedUser = userName,    // 操作人名称
                                Operate = entity.State.ToString(),  // 操作方式：新增、更新、删除
                                BatchNo = batchNo

                            });
                        }
                    }

                }

            }
        }
        /// <summary>
        /// 构建租户Id以及假删除过滤器
        /// </summary>
        /// <param name="entityBuilder"></param>
        /// <param name="dbContext"></param>
        /// <param name="isDeletedKey"></param>
        /// <param name="filterValue"></param>
        /// <returns></returns>
        protected static LambdaExpression TenantIdAndFakeDeleteQueryFilterExpression(EntityTypeBuilder entityBuilder, DbContext dbContext, string onTableTenantId = null, string isDeletedKey = null, object filterValue = null)
        {
            onTableTenantId ??= "TenantId";
            isDeletedKey ??= "IsDeleted";
            IMutableEntityType metadata = entityBuilder.Metadata;
            if (metadata.FindProperty(onTableTenantId) == null && metadata.FindProperty(isDeletedKey) == null)
            {
                return null;
            }

            Expression finialExpression = Expression.Constant(true);
            ParameterExpression parameterExpression = Expression.Parameter(metadata.ClrType, "u");

            // 租户过滤器
            if (entityBuilder.Metadata.ClrType.BaseType.Name == typeof(DEntityTenant).Name)
            {
                if (metadata.FindProperty(onTableTenantId) != null)
                {
                    ConstantExpression constantExpression = Expression.Constant(onTableTenantId);
                    MethodCallExpression right = Expression.Call(Expression.Constant(dbContext), dbContext.GetType().GetMethod("GetTenantId"));
                    finialExpression = Expression.AndAlso(finialExpression, Expression.Equal(Expression.Call(typeof(EF), "Property", new Type[1]
                    {
                        typeof(object)
                    }, parameterExpression, constantExpression), right));
                }
            }

            // 假删除过滤器
            if (metadata.FindProperty(isDeletedKey) != null)
            {
                ConstantExpression constantExpression = Expression.Constant(isDeletedKey);
                ConstantExpression right = Expression.Constant(filterValue ?? false);
                var fakeDeleteQueryExpression = Expression.Equal(Expression.Call(typeof(EF), "Property", new Type[1]
                {
                    typeof(bool)
                }, parameterExpression, constantExpression), right);
                finialExpression = Expression.AndAlso(finialExpression, fakeDeleteQueryExpression);
            }

            return Expression.Lambda(finialExpression, parameterExpression);
        }


    }
}